﻿

using System.Windows.Controls.WpfPropertyGrid.Attributes;
using System.Windows.Controls.WpfPropertyGrid.Controls;

namespace System.Windows.Controls.WpfPropertyGrid
{
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Windows.Input;

    /// <summary>
    /// Defines a collection of value Editors (Type, Category and Property editors).
    /// </summary>
    public class EditorCollection : Collection<Editor>
    {
        #region Constants and Fields

        private static readonly Dictionary<Type, Editor> Cache = new Dictionary<Type, Editor>
            {
                { typeof(Boolean), new TypeEditor(typeof(Boolean), EditorKeys.BooleanEditorKey) },
                { KnownTypes.Wpf.FontStretch, new TypeEditor(KnownTypes.Wpf.FontStretch, EditorKeys.EnumEditorKey) },
                { KnownTypes.Wpf.FontStyle, new TypeEditor(KnownTypes.Wpf.FontStyle, EditorKeys.EnumEditorKey) },
                { KnownTypes.Wpf.FontWeight, new TypeEditor(KnownTypes.Wpf.FontWeight, EditorKeys.EnumEditorKey) },
                { KnownTypes.Wpf.Cursor, new TypeEditor(KnownTypes.Wpf.Cursor, EditorKeys.EnumEditorKey) },
                { KnownTypes.Wpf.FontFamily, new TypeEditor(KnownTypes.Wpf.FontFamily, EditorKeys.FontFamilyEditorKey) },
                { KnownTypes.Wpf.Brush, new TypeEditor(KnownTypes.Wpf.Brush, EditorKeys.BrushEditorKey) },
                { typeof(Enum), new TypeEditor(typeof(Enum), EditorKeys.EnumEditorKey) },
                { typeof(ExtendSelector), new TypeEditor(typeof(ExtendSelector), EditorKeys.ExtendSelectorEditorKey) },
                { typeof(TextEditSelector), new TypeEditor(typeof(TextEditSelector), EditorKeys.TextEditSelectorEditorKey) },
                {
                    typeof(ReadOnlyCollection<ICommand>),
                    new TypeEditor(typeof(ReadOnlyCollection<ICommand>), EditorKeys.CommandsBindingEditorKey)
                },

                { typeof(Control), new TypeEditor(typeof(Control), EditorKeys.UserControlEditorKey) },
                { typeof(IList), new TypeEditor(typeof(IList), EditorKeys.CollectionEditorKey) },
                { typeof(Point), new TypeEditor(typeof(Point), EditorKeys.PointPropertyEditorKey) },
                { typeof(DateTime), new TypeEditor(typeof(DateTime), EditorKeys.DateTimePropertyEditorKey) },
                { typeof(double), new TypeEditor(typeof(double), EditorKeys.DoubleEditorKey) },
                  { typeof(IDictionary<string,object>), new TypeEditor(typeof(IDictionary<string,object>), EditorKeys.DictionaryEditorKey) },

            };

        #endregion

        #region Public Methods

        /// <summary>
        /// Gets the category editor by attributes.
        /// </summary>
        /// <param name="declaringType">Type of the declaring.</param>
        /// <param name="categoryName">Name of the category.</param>
        /// <returns>Editor for Category</returns>
        public static Editor GetCategoryEditorByAttributes(Type declaringType, string categoryName)
        {
            if (declaringType == null || string.IsNullOrEmpty(categoryName))
            {
                return null;
            }

            string name = categoryName.ToUpperInvariant();

            CategoryEditorAttribute attribute =
                declaringType.GetCustomAttributes(KnownTypes.Attributes.CategoryEditorAttribute, true).OfType
                    <CategoryEditorAttribute>().FirstOrDefault(attr => attr.CategoryName == name);

            if (attribute == null)
            {
                return null;
            }

            try
            {
                Type editorType = Type.GetType(attribute.EditorType);
                if (editorType == null || !KnownTypes.Wpg.Editor.IsAssignableFrom(editorType))
                {
                    return null;
                }
                return (Editor)Activator.CreateInstance(editorType);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the property editor by attributes.
        /// </summary>
        /// <param name="attributes">The attributes.</param>
        /// <returns>Editor for Property</returns>
        public static Editor GetPropertyEditorByAttributes(AttributeCollection attributes)
        {
            if (attributes == null)
            {
                return null;
            }

            var attribute = attributes[KnownTypes.Attributes.PropertyEditorAttribute] as PropertyEditorAttribute;
            if (attribute == null)
            {
                return null;
            }

            try
            {

                Type editorType = Type.GetType("System.Windows.Controls.WpfPropertyGrid.Controls." +attribute.EditorType);
                if (editorType == null || !KnownTypes.Wpg.Editor.IsAssignableFrom(editorType))
                {
                   
                        return null;
                

                }
                return (Editor)Activator.CreateInstance(editorType);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Finds the category editor.
        /// </summary>
        /// <param name="declaringType">Declaring type.</param>
        /// <param name="categoryName">Name of the category.</param>
        /// <returns>Editor for Category</returns>
        public CategoryEditor FindCategoryEditor(Type declaringType, string categoryName)
        {
            if (declaringType == null)
            {
                throw new ArgumentNullException("declaringType");
            }
            if (string.IsNullOrEmpty(categoryName))
            {
                throw new ArgumentNullException("categoryName");
            }

            return
                this.OfType<CategoryEditor>().Where(item => item.DeclaringType.IsAssignableFrom(declaringType)).
                    FirstOrDefault(item => item.CategoryName == categoryName);
        }

        /// <summary>
        /// Finds the property editor.
        /// </summary>
        /// <param name="declaringType">Declaring type.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns>Editor for Property</returns>
        public PropertyEditor FindPropertyEditor(Type declaringType, string propertyName)
        {
            if (declaringType == null)
            {
                throw new ArgumentNullException("declaringType");
            }
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName");
            }

            return
                this.OfType<PropertyEditor>().Where(item => item.DeclaringType.IsAssignableFrom(declaringType)).
                    FirstOrDefault(item => item.PropertyName == propertyName);
        }

        /// <summary>
        /// Finds the type editor.
        /// </summary>
        /// <param name="editedType">Edited type.</param>
        /// <returns>Editor for Type</returns>
        public TypeEditor FindTypeEditor(Type editedType)
        {
            if (editedType == null)
            {
                throw new ArgumentNullException("editedType");
            }

            return this.OfType<TypeEditor>().FirstOrDefault(item => item.EditedType.IsAssignableFrom(editedType));
        }

        /// <summary>
        /// Gets the editor.
        /// </summary>
        /// <param name="categoryItem">The category item.</param>
        /// <returns>Editor for Category</returns>
        public Editor GetEditor(CategoryItem categoryItem)
        {
            if (categoryItem == null)
            {
                throw new ArgumentNullException("categoryItem");
            }

            if (categoryItem.Owner == null)
            {
                return null;
            }

            object declaringObject = ObjectServices.GetUnwrappedObject(categoryItem.Owner.SelectedObject);
            if (declaringObject == null)
            {
                return null;
            }

            Type declaringType = declaringObject.GetType();

            Editor editor = this.FindCategoryEditor(declaringType, categoryItem.Name);
            if (editor != null)
            {
                return editor;
            }

            editor = GetCategoryEditorByAttributes(declaringType, categoryItem.Name);
            if (editor != null)
            {
                return editor;
            }

            return new CategoryEditor(declaringType, categoryItem.Name, EditorKeys.DefaultCategoryEditorKey);
        }

        /// <summary>
        /// Gets the editor.
        /// </summary>
        /// <param name="propertyItem">The property item.</param>
        /// <returns>Editor for Property</returns>
        public Editor GetEditor(PropertyItem propertyItem)
        {
            if (propertyItem == null)
            {
                throw new ArgumentNullException("propertyItem");
            }

            Editor editor;

            if (propertyItem.Attributes != null)
            {
                editor = GetPropertyEditorByAttributes(propertyItem.Attributes);
                if (editor != null)
                {
                    return editor;
                }
            }

            if (propertyItem.Component != null && !string.IsNullOrEmpty(propertyItem.Name))
            {
                object declaringObject = ObjectServices.GetUnwrappedObject(propertyItem.Owner.SelectedObject);
                editor = this.FindPropertyEditor(declaringObject.GetType(), propertyItem.Name);
                if (editor != null)
                {
                    return editor;
                }
            }

            if (propertyItem.PropertyValue.HasSubProperties)
            {
                return new TypeEditor(propertyItem.PropertyType, EditorKeys.ComplexPropertyEditorKey);
            }

            bool hasType = propertyItem.PropertyType != null;

            if (hasType)
            {
                editor = this.FindTypeEditor(propertyItem.PropertyType);
                if (editor != null)
                {
                    {
                        return editor;
                    }
                }

                if (hasType)
                {
                    foreach (var cachedEditor in Cache)
                    {
                        if (cachedEditor.Key.IsAssignableFrom(propertyItem.PropertyType))
                        {
                            return cachedEditor.Value;
                        }
                    }

                    return new TypeEditor(propertyItem.PropertyType, EditorKeys.DefaultEditorKey);
                }


            }
            return null;
        }

        #endregion

    }


}
  